package com.ganzi.ganziserver.conf.rabbit;

import com.ganzi.ganziserver.utils.Topic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * 配置文件中获取学校数量，然后动态添加交换机和队列
 * */
@Component
public class RabbitQueueBeanRegister implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
    Logger log = LoggerFactory.getLogger(RabbitQueueBeanRegister.class);
    private ApplicationContext applicationContext;
    private BeanDefinitionRegistry beanDefinitionRegistry;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        this.beanDefinitionRegistry = beanDefinitionRegistry;
        //声明交换机
        declareExchange();
        //声明队列和绑定
        declareQueueAndBinding();
    }

    /**
     * 声明交换机
     */
    private void declareExchange() {
        beanDefinitionRegistry.registerBeanDefinition(Topic.EXCHANGE_FOR_CONTROL, BeanDefinitionBuilder.genericBeanDefinition(TopicExchange.class, () -> (TopicExchange) ExchangeBuilder
                .topicExchange(Topic.EXCHANGE_FOR_CONTROL).durable(true).build()).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.EXCHANGE_FOR_RECEIVE, BeanDefinitionBuilder.genericBeanDefinition(TopicExchange.class, () -> (TopicExchange) ExchangeBuilder
                .topicExchange(Topic.EXCHANGE_FOR_RECEIVE).durable(true).build()).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.DEAD_LETTER_EXCHANGE, BeanDefinitionBuilder.genericBeanDefinition(DirectExchange.class, () -> (DirectExchange) ExchangeBuilder
                .directExchange(Topic.DEAD_LETTER_EXCHANGE).durable(true).build()).getBeanDefinition());
    }

    /**
     * 声明队列和绑定
     */
    private void declareQueueAndBinding() {
        //注册学校队列
        int count = Integer.parseInt(((ConfigurableApplicationContext)applicationContext).getEnvironment().getProperty("rabbit.count"));
        if(count <= 0)count = 5;
        log.info("initialization rabbitmq queue: count->"+count);
        for (int a=0 ; a<count ; a++) {
            //注册所有队列
            String key = Topic.BASE+(a+1);
            beanDefinitionRegistry.registerBeanDefinition(key, BeanDefinitionBuilder.genericBeanDefinition(Queue.class, () -> {
                Queue queue;
                queue = QueueBuilder.durable(key)
                        .withArgument("x-dead-letter-exchange", Topic.DEAD_LETTER_EXCHANGE)
                        .withArgument("x-dead-letter-routing-key", Topic.DEAD_ROUTING_KEY)
                        .build();
                return queue;
            }).getBeanDefinition());
            //注册队列与交换机的绑定
            beanDefinitionRegistry.registerBeanDefinition(key+ Topic.BINDING_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () -> BindingBuilder
                .bind(applicationContext.getBean(key, Queue.class))
                .to(applicationContext.getBean(Topic.EXCHANGE_FOR_CONTROL, TopicExchange.class))
                .with(key)).getBeanDefinition());
            beanDefinitionRegistry.registerBeanDefinition(key+ Topic.STATE_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () -> BindingBuilder
                .bind(applicationContext.getBean(key, Queue.class))
                .to(applicationContext.getBean(Topic.EXCHANGE_FOR_CONTROL, TopicExchange.class))
                .with(Topic.SCHOOL_STATE)).getBeanDefinition());
        }


        //注册接收队列
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_ONE, BeanDefinitionBuilder.genericBeanDefinition(Queue.class, () -> {
            Queue queue;
            queue = QueueBuilder.durable(Topic.RECEVIE_ONE).build();
            return queue;
        }).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_TOW, BeanDefinitionBuilder.genericBeanDefinition(Queue.class, () -> {
            Queue queue;
            queue = QueueBuilder.durable(Topic.RECEVIE_TOW).build();
            return queue;
        }).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_THREE, BeanDefinitionBuilder.genericBeanDefinition(Queue.class, () -> {
            Queue queue;
            queue = QueueBuilder.durable(Topic.RECEVIE_THREE).build();
            return queue;
        }).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_STATE, BeanDefinitionBuilder.genericBeanDefinition(Queue.class, () -> {
            Queue queue;
            queue = QueueBuilder.durable(Topic.RECEVIE_STATE).build();
            return queue;
        }).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_ONE+ Topic.BINDING_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () -> BindingBuilder
                .bind(applicationContext.getBean(Topic.RECEVIE_ONE, Queue.class))
                .to(applicationContext.getBean(Topic.EXCHANGE_FOR_RECEIVE, TopicExchange.class))
                .with(Topic.RECEVIE_ONE)).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_TOW+ Topic.BINDING_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () -> BindingBuilder
                .bind(applicationContext.getBean(Topic.RECEVIE_TOW, Queue.class))
                .to(applicationContext.getBean(Topic.EXCHANGE_FOR_RECEIVE, TopicExchange.class))
                .with(Topic.RECEVIE_TOW)).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_THREE+ Topic.BINDING_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () -> BindingBuilder
                .bind(applicationContext.getBean(Topic.RECEVIE_THREE, Queue.class))
                .to(applicationContext.getBean(Topic.EXCHANGE_FOR_RECEIVE, TopicExchange.class))
                .with(Topic.RECEVIE_THREE)).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.RECEVIE_STATE+ Topic.BINDING_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () -> BindingBuilder
                .bind(applicationContext.getBean(Topic.RECEVIE_STATE, Queue.class))
                .to(applicationContext.getBean(Topic.EXCHANGE_FOR_RECEIVE, TopicExchange.class))
                .with(Topic.RECEVIE_STATE)).getBeanDefinition());

        //注册死信箱
        beanDefinitionRegistry.registerBeanDefinition(Topic.DEAD_ROUTING_KEY, BeanDefinitionBuilder.genericBeanDefinition(Queue.class, () -> {
            Queue queue;
            queue = QueueBuilder.durable(Topic.DEAD_ROUTING_KEY).build();
            return queue;
        }).getBeanDefinition());
        beanDefinitionRegistry.registerBeanDefinition(Topic.DEAD_ROUTING_KEY+ Topic.BINDING_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () -> BindingBuilder
                .bind(applicationContext.getBean(Topic.DEAD_ROUTING_KEY, Queue.class))
                .to(applicationContext.getBean(Topic.DEAD_LETTER_EXCHANGE, DirectExchange.class))
                .with(Topic.DEAD_ROUTING_KEY)).getBeanDefinition());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


}
